package cn.com.pc.content.aspect;

import cn.com.pc.content.annotation.RedisLock;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.http.HttpStatus;
import org.springframework.integration.redis.util.RedisLockRegistry;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ResponseStatusException;

import java.lang.reflect.Method;
import java.util.concurrent.locks.Lock;

/**
 * redis分布式锁切面，用于对整个方法加锁
 */
@Slf4j
@Aspect
@Component
public class RedisLockAspect {

    @Autowired
    private RedisLockRegistry redisLockRegistry;

    @Around(value = "@annotation(redisLock)")
    public Object redisLock(ProceedingJoinPoint joinPoint,
                            RedisLock redisLock) throws Throwable {
        Object output = null;
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Object[] arguments = joinPoint.getArgs();

        // 获取锁的key
        Object lockKey = StringUtils.hasLength(redisLock.lockKey()) ? redisLock.lockKey() : parseKey(method, arguments);
        Lock lock = redisLockRegistry.obtain(lockKey);

        boolean ifLock = lock.tryLock();
        if (ifLock) {
            try {
                // 具体执行
                output = joinPoint.proceed();
            } catch (Throwable throwable) {
                throwable.printStackTrace();
                output = throwable;
            } finally {
                if (lock.tryLock()) {
                    lock.unlock();
                }
            }
        } else {
            log.info("线程[{}]执行方法[{}]，获取锁：{}", Thread.currentThread().getName(), method.toString(), ifLock);
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "已有任务正在执行，请勿重复请求");
        }
        if (output instanceof Throwable) {
            throw (Throwable) output;
        }
        return output;
    }

    /**
     * 解析lockKey
     */
    private Object parseKey(Method method, Object[] args) {
        LocalVariableTableParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
        String[] parameterNames = parameterNameDiscoverer.getParameterNames(method);
        // 构造上下文
        int parameterLength = parameterNames == null ? 0 : parameterNames.length;
        StringBuilder key = new StringBuilder(method.toString());
        if (args.length == parameterLength && parameterLength > 0) {
            for (int i = 0, len = args.length; i < len; i++) {
                // 使用setVariable方法来注册自定义变量
                key.append(",").append(parameterNames[i]).append("=").append(args[i]);
            }
        }
        return key.toString();
    }
}
